# coding: utf-8

# -------------------------------------------------------------------------------
# Name:         http_testsuite_runner.py
# Description:
# Author:       XiangjunZhao
# EMAIL:        2419352654@qq.com
# Date:         2019/11/18 17:32
# -------------------------------------------------------------------------------
import logging
import time
from django.utils import timezone
from apps.HttpAutoTestService.core.ext_method_runner import ExtMethodRunner
from apps.HttpAutoTestService.core.http_client import HttpSession
from apps.HttpAutoTestService.core.http_session_context import HttpSessionContext
from apps.HttpAutoTestService.core.http_testcase_runner import HttpTestcaseRunner
from apps.HttpAutoTestService.core.script_runner import ScriptRunner
from apps.HttpAutoTestService.core.sql_runner import SQLRunner
from apps.HttpAutoTestService.models import TestsuiteResult, Environment, Project, Testsuite, Testsuite2Testcase, \
    TestcaseResult, ExtMethodResult, SQLResult

logger = logging.getLogger(__name__)


class HttpTestsuiteRunner(object):

    def __init__(self, http_session: HttpSession = None, http_session_context: HttpSessionContext = None,
                 environment: Environment = None, project: Project = None, testsuite: Testsuite = None):
        """
        实例化场景运行器
        Args:
            http_session:
            http_session_context:
            environment: 运行环境
            project: 项目
            testsuite: 场景
        """
        self.http_session = http_session
        self.http_session_context = http_session_context
        self.environment = environment
        self.project = project
        self.testsuite = testsuite

    def run_http_api(self, testsuite2testcase: Testsuite2Testcase, executor, is_periodictask) -> TestcaseResult:
        """
        执行http接口用例
        """
        # 执行逻辑控制——前置脚本
        logic_control = testsuite2testcase.logic_control
        if logic_control and logic_control.get('pre_script'):
            pre_script = testsuite2testcase.pre_script
            ScriptRunner(http_session_context=self.http_session_context).run(pre_script)

        # 1、获取场景用例所属接口
        api = testsuite2testcase.api
        # 2、获取接口运行环境
        environment = api.environment
        if environment:
            # 3、获取运行环境全局参数
            environment_variables = environment.global_variables
            # 4、将运行环境全局参数更新到http_session_context中
            self.http_session_context.update_session_variables(environment_variables)
        else:
            environment = self.environment
        # 执行http接口用例
        http_testcase_runner = HttpTestcaseRunner(http_session=self.http_session,
                                                  http_session_context=self.http_session_context,
                                                  environment=environment,
                                                  project=self.project,
                                                  api=api)

        # 运行测试场景时，测试用例的数据来源于从testcase中冗余到testsuite2testcase中的数据
        testcase_name = testsuite2testcase.testcase_name
        url = testsuite2testcase.url
        headers = testsuite2testcase.headers
        request_params = testsuite2testcase.request_params
        request_data_type = testsuite2testcase.request_data_type
        request_data = testsuite2testcase.request_data
        expect_result = testsuite2testcase.expect_result
        testcase_result = http_testcase_runner.run(testcase_name=testcase_name,
                                                   url=url,
                                                   headers=headers,
                                                   request_params=request_params,
                                                   request_data_type=request_data_type,
                                                   request_data=request_data,
                                                   expect_result=expect_result,
                                                   executor=executor,
                                                   is_periodictask=is_periodictask)
        # 执行逻辑控制——后置脚本
        logic_control = testsuite2testcase.logic_control
        if logic_control and logic_control.get('post_script'):
            post_script = testsuite2testcase.post_script
            ScriptRunner(http_session_context=self.http_session_context).run(
                post_script, ('req_url', 'req_method', 'req_headers', 'req_data', 'resp_code', 'resp_data'),
                testcase_result.url, testcase_result.method, testcase_result.headers, testcase_result.request_data,
                testcase_result.actual_status_code, testcase_result.actual_response_data)
        return testcase_result

    def run_ext_method(self, testsuite2testcase: Testsuite2Testcase, executor, is_periodictask) -> ExtMethodResult:
        """
        执行扩展方法
        """
        # 执行扩展方法
        ext_method_name = testsuite2testcase.ext_method_name
        ext_method = testsuite2testcase.ext_method
        expect_result = testsuite2testcase.expect_result
        # 初始化扩展方法执行器
        ext_method_runner = ExtMethodRunner()
        ext_method_result = ext_method_runner.run(http_session=self.http_session,
                                                  http_session_context=self.http_session_context,
                                                  ext_method_name=ext_method_name,
                                                  ext_method=ext_method,
                                                  expect_result=expect_result,
                                                  executor=executor,
                                                  is_periodictask=is_periodictask)

        ext_method_result.testsuite_name = self.testsuite.name
        return ext_method_result

    def run_sql(self, testsuite2testcase: Testsuite2Testcase, executor, is_periodictask) -> SQLResult:
        """
        执行数据库sql
        """
        expect_result = testsuite2testcase.expect_result
        sql = testsuite2testcase.sql
        sql_desc = testsuite2testcase.sql_desc
        sql_ext = testsuite2testcase.sql_ext
        sql_result = SQLRunner.run(http_session_context=self.http_session_context, sql=sql, sql_desc=sql_desc,
                                   expect_result=expect_result, executor=executor, is_periodictask=is_periodictask,
                                   **sql_ext)
        sql_result.testsuite_name = self.testsuite.name
        return sql_result

    def run(self, executor=None, is_periodictask=True):
        logger.info('*************** 执行测试场景:【{name}】***************'.format(name=self.testsuite.name))
        # 执行时间
        execute_time = timezone.now()
        # 开始时间，毫秒
        start_ms = time.time() * 1000
        testsuite2testcase_list = self.testsuite.testsuite2testcase.all()
        testsuite_result_mapping = {
            "execute_time": execute_time,
            "elapsed_ms": 0,
            "passed_num": 0,
            "failed_num": 0,
            "total_num": 0,
            "status": "PASS",
            "project_id": self.testsuite.project.id,
            "project_name": self.testsuite.project.name,
            "testsuite_id": self.testsuite.id,
            "testsuite_name": self.testsuite.name,
        }

        if not is_periodictask:
            testsuite_result_mapping.update({
                'executor_id': executor.id,
                'executor_real_name': executor.real_name,
                'is_periodictask': is_periodictask
            })

        testcase_result_list = []
        ext_method_result_list = []
        sql_result_list = []
        for testsuite2testcase in testsuite2testcase_list:
            # 循环执行次数
            loop_count = testsuite2testcase.loop_count
            # 执行 / 跳过
            is_execute = testsuite2testcase.is_execute
            if is_execute:
                if testsuite2testcase.type == 'HTTP_API':
                    temp_testcase_result_list = []
                    for i in range(loop_count):
                        testcase_result = self.run_http_api(testsuite2testcase, executor, is_periodictask)
                        testcase_result.testsuite_name = self.testsuite.name
                        temp_testcase_result_list.append(testcase_result)
                    testcase_result_list.extend(temp_testcase_result_list)
                    testsuite_result_mapping["total_num"] += len(temp_testcase_result_list)
                    for temp_testcase_result in temp_testcase_result_list:
                        if temp_testcase_result is None or temp_testcase_result.status == "FAIL":
                            testsuite_result_mapping["failed_num"] += 1
                        elif temp_testcase_result and temp_testcase_result.status == "PASS":
                            testsuite_result_mapping["passed_num"] += 1
                elif testsuite2testcase.type == 'EXT_METHOD':
                    temp_ext_method_result_list = []
                    for i in range(loop_count):
                        ext_method_result = self.run_ext_method(testsuite2testcase, executor, is_periodictask)
                        ext_method_result.testsuite_name = self.testsuite.name
                        temp_ext_method_result_list.append(ext_method_result)
                    ext_method_result_list.extend(temp_ext_method_result_list)
                    testsuite_result_mapping["total_num"] += len(temp_ext_method_result_list)
                    for temp_ext_method_result in temp_ext_method_result_list:
                        if temp_ext_method_result is None or temp_ext_method_result.status == "FAIL":
                            testsuite_result_mapping["failed_num"] += 1
                        elif temp_ext_method_result and temp_ext_method_result.status in ("PASS", None):
                            # None：表示扩展方法不进行校验，比如sleep方法
                            testsuite_result_mapping["passed_num"] += 1
                elif testsuite2testcase.type == 'SQL':
                    sql_result = self.run_sql(testsuite2testcase, executor, is_periodictask)
                    sql_result.testsuite_name = self.testsuite.name
                    sql_result_list.append(sql_result)
                    testsuite_result_mapping["total_num"] += 1
                    if sql_result is None or sql_result.status == "FAIL":
                        testsuite_result_mapping["failed_num"] += 1
                    elif sql_result and sql_result.status == "PASS":
                        testsuite_result_mapping["passed_num"] += 1
                elif testsuite2testcase.type == 'LOGIC_CONTROL':
                    pass
                # 更新场景结果状态
                if testsuite_result_mapping["passed_num"] == testsuite_result_mapping["total_num"]:
                    testsuite_result_mapping["status"] = "PASS"
                elif testsuite_result_mapping["failed_num"] > 0 and testsuite_result_mapping["passed_num"] > 0:
                    testsuite_result_mapping["status"] = "PARTIAL_PASS"
                elif testsuite_result_mapping["failed_num"] == testsuite_result_mapping["total_num"]:
                    testsuite_result_mapping["status"] = "FAIL"
        # 结束时间，毫秒
        end_ms = time.time() * 1000
        # 执行耗时，毫秒
        elapsed_ms = round(end_ms - start_ms, 3)
        testsuite_result_mapping["elapsed_ms"] = elapsed_ms
        testsuite_result = TestsuiteResult(**testsuite_result_mapping)
        return testsuite_result, testcase_result_list, ext_method_result_list, sql_result_list
